<?php
namespace app\common\service;

use think\Cache;
use app\model\UserProgram;
use app\model\GambleRecord;
use app\model\GambleProduct;
use app\model\GambleUserRecord;
use app\model\GambleMoneyLog;
use app\util\Tools;
use think\Db;

/**
 * 拼团模块
 */
class PinTuan{

    protected static $USER_LIST_KEY = 'group_pintuan_user_list';

    protected static $WAIT_USER_LIST_KEY = 'wait_group_pintuan_user_list';

    protected static $STATUS_KEY = 'group_pintuan_status';

   // protected static $CAL_ADVANCE_TIME = 2;  //开奖计算提前结束拼团时间

    //当前开团信息
    protected $groupInfo = null;

    protected $status = null;  // 1 拼团中、2 等待开团、3 中奖计算中


    public function __construct(){
        $this->status = Cache::store('redis_pintuan')->get(self::$STATUS_KEY);
        if(empty($groupInfo) && ($this->status == 1 || $this->status == 3)){
            $this->groupInfo = GambleRecord::where(['status' => 1])->find();
        }
    }

    //获取开团状态
    public function checkState(){
        $expiresTime = $this->status == 1 ?  $this->groupInfo['overTime'] - time() : 0;  //开奖倒计时
        return [
            'status' => $this->status,
            'expiresTime' => $expiresTime
        ];

    }

    /**
     * 获取用户拼团信息
     * @param int userId 用户ID
     * @return array [
     *    userId => 用户ID
     *    status => 开团状态
     *    pintuan_list => 参与的拼团列表
     *    expiresTime => 拼团结束剩余时间
     * ]
     */
    public function getInfo($userId){
        $expiresTime = $this->status == 1 ?  $this->groupInfo['overTime'] - time() : 0;  //开奖倒计时
        $userIds =
        $list = [];
        if($userIds && in_array($userId, $userIds)){   //判断用户是否在拼团中
            $list = GambleUserRecord::where(['userID' => $userId, 'status' => 1])->select()->toArray();
        }
        return [
            'userId' => $userId,
            'status' => $this->status,
            'pintuan_list' => $list,
            'expiresTime' => $expiresTime
        ];
    }

    //开启拼团
    public function start(){
        if($this->status != 2 && !empty($this->status)){
            return false;
        }
        $config = self::getConfig();
        if(empty($config)){
            return false;
        }
        $model = new GambleRecord;
        $data = [
            'name' => date("Y-m-d H:i:s"). '至'. date("Y-m-d H:i:s", time() + $config['GROUP_TIME']) . '拼团',
            'overTime' => time() + $config['GROUP_TIME'],
            'status' => 1,
            'addDate' => date("Ymd"),
            'addMonth' => date("Ym"),
        ];
        $res = $model->create($data);
        if($res){
            $data['id'] = $model->getLastInsID();
            $this->groupInfo = $data;
            Cache::store('redis_pintuan')->set(self::$STATUS_KEY, 1);
            $this->status = 1;
        }
        return $res;
    }

    //结束开团并开奖
    public function over(){
        if($this->status != 1 || empty($this->groupInfo)){
            return;
        }
        Cache::store('redis_pintuan')->set(self::$STATUS_KEY, 3);
        $this->status = 3;
        sleep(1);
        $pingOrderIds = self::pingOrderIds();
        $res = self::calWinner($pingOrderIds);

        $model = new GambleRecord;
        $winIds = $res['winList'] ? implode(',', array_column($res['winList'], 'orderID')) : '';
        $dbRes = $model->update([
            'partakeNum' => $pingOrderIds ? count($pingOrderIds) : 0,
            'winNum' => $pingOrderIds ? count($res['winList']) : 0,
            'winUsers' => $winIds,
            'partakeUsers' => $pingOrderIds  ? implode(',', $pingOrderIds) : '',
            'status' => 2
        ], ['id' => $this->groupInfo['id']]);
        if(!$dbRes) return false;

        $UserProgram = new UserProgram;
        /**********未抽中逻辑开始***********/
        if($res['waitGrantCommission']){
            $GambleMoneyLog = new GambleMoneyLog;
            Db::startTrans();
            try{
                foreach($res['waitGrantCommission'] as $uid => $num){
                    $userInfo = $UserProgram->where('userID', $uid)->field('gambleMoney')->lock(true)->find(); //自动续拼
                    $balance = Tools::bcCalculate($userInfo['gambleMoney'], $num,'+');
                    $vv = $UserProgram->update([
                        'gambleMoney' => $balance,
                    ], ['userID' => $uid]);
                    $GambleMoneyLog->create([
                        'userID' => $uid,
                        'ordertype' => 1,
                        'amount' => $num,
                        'turnover' => 1,
                        'content' => $this->groupInfo['name'].'拼团未中奖励',
                        'balance' => $balance,
                        'relateID' => $this->groupInfo['id']
                    ]);
                }
                $orderIDs = array_column($res['noWinList'], 'orderID');
                $r = Db::execute("update a_gamble_user_record set awardMoney=awardMoney+100,awardNum=awardNum+1 where orderID in(".implode(",", $orderIDs).")");
                if(!$r){
                    throw new \Exception('更新订单奖金失败');
                }
                foreach ($res['noWinList'] as $val){
                    Cache::store('redis_pintuan')->listPush('group_ping_no_win_order', json_encode($val));
                }
                Db::commit();
            }catch(\Exception $e){
                Db::rollback();
            }
        }
        /**********未抽中逻辑结束***********/

        /*******************中奖逻辑开始*********************/
        if($res['winList']){
            $productIds = array_column($res['winList'], 'productID');
            array_multisort(array_column($res['winList'], 'orderID'), SORT_ASC, $res['winList']);
            $productsList = GambleProduct::alias('g')
            ->field('g.productID,g.salePrice,g.saleNum,p.productName')
            ->join('fresh_product p', 'p.productID=g.productID', 'LEFT')
            ->where('g.productID', 'in', $productIds)
            ->where('g.status', 1)
            ->select()
            ->toArray();
            $products = array_column($productsList,'salePrice', 'productID');
            $productsList = array_column($productsList, null, 'productID');
            $GroupMoneyLog = new \app\model\GroupMoneyLog;
            $GambleProduct = new \app\model\GambleProduct;
            foreach($res['winList'] as $var){
                $umodel = new GambleUserRecord;
                $userRecord = $umodel->get($var['orderID']);
                if($userRecord){
                    $userRecord->result = 1;
                    $userRecord->status = 2;
                    $userRecord->overTime = time();
                    $s = $userRecord->save();
                    if($s !== false){
                        self::rmPingOrderId($var['orderID']);
                        self::setWinNum($var['userID']);
                        //self::decPinCacheNum($var['userID']);
                    }else{
                        continue;
                    }
                }else{
                    continue;
                }
                Db::startTrans();
                try{
                    $userInfo = $UserProgram->where('userID', $var['userID'])->field('isReplen,groupMoney,isReplen')->lock(true)->find(); //自动续拼
                    if($userInfo && $userInfo['isReplen'] == 1){
                        if(isset($products[$var['productID']]) && $userInfo['groupMoney'] >= $products[$var['productID']]){
                            $userRecord->orderID = null;
                            $userRecord->result = 2;
                            $userRecord->status = 1;
                            $userRecord->overTime = 0;
                            $userRecord->tradeNo = Tools::orderNo();
                            $userRecord->remarks = '自动续拼';
                            $userRecord->payTime = time();
                            $userRecord->payType = 2;
                            $userRecord->money = $products[$var['productID']];
                            $userRecord->amount = $products[$var['productID']];
                            $userRecord->dealType = 0;
                            $userRecord->awardMoney = 0;
                            $userRecord->awardNum = 0;
                            $userRecord->isUpdate(false)->save();
                            $newOrderId = $userRecord->getLastInsID();
                            $balance = Tools::bcCalculate($userInfo['groupMoney'], $products[$var['productID']],'-');
                            $UserProgram->update([
                                'groupMoney' => $balance,
                            ], ['userID' => $var['userID']]);

                            Db::commit();
                            $GroupMoneyLog->create([
                                'userID' => $var['userID'],
                                'ordertype' => 2,
                                'amount' => $products[$var['productID']],
                                'turnover' => -1,
                                'content' => '自动续费拼团商品-' . $productsList[$var['productID']]['productName'],
                                'balance' => $balance,
                                'relateID' => $newOrderId
                            ]);
                            $GambleProduct->update([
                                 'saleNum' => $productsList[$var['productID']]['saleNum'] + 1
                            ],['productID' => $var['productID']]);
                            $this->userJoin($newOrderId, false);
                            continue;
                        }
                    }
                    Db::rollback();
                    continue;
                }catch(\Exception $e){
                    Db::rollback();
                    continue;
                }
            }
        }
        /*********************中奖逻辑结束******************/
        Cache::store('redis_pintuan')->set(self::$STATUS_KEY, 2);
        $this->status = 2;
        return $res['winList'];
    }

    //计算中奖用户
    protected static function calWinner($orderIDs){
        if(empty($orderIDs)){
            return true;
        }

        $list = GambleUserRecord::where('orderID', 'in', $orderIDs)
        ->field('orderID,userID,productID')
        ->where('status', 1)
        ->select()
        ->toArray();
        $userIDs = array_unique(array_column($list, 'userID'));
        $config = self::getConfig();
        if($config['GROUP_FIRST_DAY'] != 1){
            $oldUserIds = GambleUserRecord::field('userID') //老用户查询
            ->where('userID', 'in', $userIDs)
            ->where('addTime', '<', strtotime(date("Y-m-d")))
            ->where('status', '>', -1)
            ->group('userID')
            ->select()
            ->toArray();
            $oldUserIds = array_column($oldUserIds, 'userID');
        }else{
            $oldUserIds = [];
        }

        $maxWinUserIds = [];
        $winNum = self::getWin();
        foreach ($winNum as $uid => $num){
            if($num >= intval($config['GROUP_MAX_DAY_NUM'])){
                $maxWinUserIds[] = $uid;
            }
        }

        $waitCal = [];  //待抽签列表
        $waitGrantCommission = [];  //待发放佣金信息
        $winList = [];    //中签列表
        $noWinList = [];  //未中列表
        foreach ($list as $var){
            if(in_array($var['userID'], $maxWinUserIds) || ($config['GROUP_FIRST_DAY'] != 1 && !in_array($var['userID'], $oldUserIds))){
                $waitGrantCommission[$var['userID']] = isset($waitGrantCommission[$var['userID']]) ? $waitGrantCommission[$var['userID']] + 1 : 1;
                $noWinList[] = $var; 
            }else{
                $waitCal[] = $var;
            }
        }
        if($waitCal){
            $rate = intval(self::getConfig('GROUP_RATE'));
            $rate  = $rate < 1 ? 0.05 : $rate/100;
            $num = count($waitCal);
            $winNum = ceil($num * $rate);
            if($winNum >= count($waitCal)){
                $winList = $waitCal;
            }else{
                if($winNum < count($waitCal)/2){
                    while(true){
                        shuffle($waitCal);
                        $winList[] = array_shift($waitCal);
                        if(count($winList) == $winNum){
                            foreach($waitCal as $v){
                                $waitGrantCommission[$v['userID']] = isset($waitGrantCommission[$v['userID']]) ? $waitGrantCommission[$v['userID']] + 1 : 1;
                                $noWinList[] = $v;
                            }
                            break;
                        }
                    }
                }else{
                    while(true){
                        shuffle($waitCal);
                        $v = array_shift($waitCal);
                        $waitGrantCommission[$v['userID']] = isset($waitGrantCommission[$v['userID']]) ? $waitGrantCommission[$v['userID']] + 1 : 1;
                        $noWinList[] = $v;
                        if(count($waitCal) == $winNum){
                            $winList = $waitCal;
                            break;
                        }
                    }
                }
            }

        }
        return compact('waitGrantCommission', 'winList', 'noWinList');
    }

    /**
     * 用户加入拼团
     * @throws new exception
     */
    public function userJoin($orderID, $updAll = true){
        if(empty($this->status)){
            throw new \Exception('活动还未开始');
        }
        $userRecord = new GambleUserRecord;
        $order = $userRecord->get($orderID);
        if(empty($order) || empty($order->payTime) || $order->refundStatus == 2 || $order->refundStatus == 1){
            throw new \Exception('0201-拼团订单不存在');
        }
        //self::addPinCacheNum($order->userID, 30);
        self::setPingOrderIds($orderID);
        if($updAll){
            $res = $userRecord->update([
                'status' => 1,
            ],['orderID' => $orderID]);
            if(!$res){
                self::rmPingOrderId($orderID);
                throw new \Exception('加入拼团失败请重试');
                return false;
            }
        }
        return true;
    }

    /**
     * 用户退出拼团
     */
    public function userOut($orderID, $updAll = true){
        if($this->status == 3){
            throw new \Exception('拼团结果计算中不能退出拼团');
            return false;
        }
        $userRecord = new GambleUserRecord;
        $record = $userRecord->where(['orderID' => $orderID,  'status' => 1])->find();
        if(empty($record)){
            throw new \Exception('拼团订单不存在');
            return false;
        }
        self::rmPingOrderId($orderID);
        if($updAll){
            $res = $userRecord->update([
                'status' => 2,
                'remarks' => '用户主动退出拼团'
            ],['orderID' => $orderID]);
            if(!$res){
                self::setPingOrderIds($orderID);
                throw new \Exception('退出拼团失败请重试');
                return false;
            }
        }
        return true;
    }

    //开团中用户
    public static function pingOrderIds(){
        $data =  Cache::store('redis_pintuan')->setMembers(self::$USER_LIST_KEY);
        return $data ? : [];
    }

    public static function rmPingOrderId($val){
        if(empty($val)) return Cache::store('redis_pintuan')->handler()->del(self::$USER_LIST_KEY);
        return Cache::store('redis_pintuan')->setMove(self::$USER_LIST_KEY, $val);
    }

    public static function setPingOrderIds($val){
        if(empty($val)) return;
        if(is_array($val)){
            $handler = Cache::store('redis_pintuan')->handler();
            array_unshift($val, self::$USER_LIST_KEY);
            return call_user_func_array([$handler, 'sAdd'], $val);
        }else{
            return Cache::store('redis_pintuan')->setAdd(self::$USER_LIST_KEY, $val);
        }
    }

    // //等待开团的用户
    // public static function waitOrderIds(){
    //     $data =  Cache::store('redis_pintuan')->setMembers(self::$WAIT_USER_LIST_KEY);
    //     return $data ? : [];
    // }

    // public static function rmWaitOrderId($val){
    //     if(empty($val)) return Cache::store('redis_pintuan')->handler()->del(self::$WAIT_USER_LIST_KEY);
    //     return Cache::store('redis_pintuan')->setMove(self::$WAIT_USER_LIST_KEY, $val);
    // }

    // public static function setWaitOrderIds($val){
    //     if(empty($val)) return;
    //     if(is_array($val)){
    //         $handler = Cache::store('redis_pintuan')->handler();
    //         array_unshift($val, self::$WAIT_USER_LIST_KEY);
    //         return call_user_func_array([$handler, 'sAdd'], $val);
    //     }else{
    //         return Cache::store('redis_pintuan')->sAdd(self::$WAIT_USER_LIST_KEY, $val);
    //     }
    // }

    // public static function mergeWaitToPing(){
    //     Cache::store('redis_pintuan')->setUnion([self::$WAIT_USER_LIST_KEY, self::$USER_LIST_KEY], self::$USER_LIST_KEY);
    //     self::rmWaitOrderId('');
    // }


    public static function setWinNum($userId, $num = 1){
        if(!Cache::store('redis_pintuan')->handler()->exists(self::$USER_LIST_KEY . '_win_num')){
            Cache::store('redis_pintuan')->handler()->hset(self::$USER_LIST_KEY . '_win_num', $userId, $num);
            $expireTime = mktime(23, 59, 59, date("m"), date("d"), date("Y"));
            Cache::store('redis_pintuan')->handler()->expireAt(self::$USER_LIST_KEY . '_win_num', $expireTime);
        }else{
            Cache::store('redis_pintuan')->hashInc(self::$USER_LIST_KEY . '_win_num', $userId, $num);
        }
    }


    public static function getWin(){
        return Cache::store('redis_pintuan')->hashGet(self::$USER_LIST_KEY . '_win_num', [], 2);
    }


    public static function addPinCacheNum($userId, $max){
        $num = Cache::store('redis_pintuan')->hashGet('user_ping_current_num', $userId);
        $num = intval($num);
        if($num >= $max){
            throw new \Exception('超过最大拼团数');
        }
        if($num <= 0){
            return Cache::store('redis_pintuan')->hashSet('user_ping_current_num', [$userId => 1]);
        }
        return Cache::store('redis_pintuan')->hashInc('user_ping_current_num', $userId, 1);
    }

    public static function decPinCacheNum($userId){
        $num = Cache::store('redis_pintuan')->hashGet('user_ping_current_num', $userId);
        if($num <= 0){
            return;
        }
        return Cache::store('redis_pintuan')->hashInc('user_ping_current_num', $userId, -1);
    }



    //清除信息
    public static function destroy(){
        Cache::store('redis_pintuan')->handler()->del(self::$USER_LIST_KEY);
        Cache::store('redis_pintuan')->handler()->del('user_ping_current_num');
        Cache::store('redis_pintuan')->handler()->del(self::$USER_LIST_KEY . '_win_num');
    }

    public static function clear(){
        Cache::store('redis_pintuan')->set(self::$STATUS_KEY, 2);
        $model = new GambleRecord;
        $dbRes = $model->update([
            'partakeNum' => 0,
            'winNum' => 0,
            'winUsers' => '',
            'partakeUsers' => '',
            'status' => 2
        ], ['status' => 1]);
    }


    public static function getConfig($key = ''){
        if(Cache::store('redis_pintuan')->handler()->exists('group_pin_tuan_config')){
            if(empty($key)){
                return Cache::store('redis_pintuan')->hashGet('group_pin_tuan_config', [], 2);
            }else{
                return Cache::store('redis_pintuan')->hashGet('group_pin_tuan_config', $key);
            }
        }else{
            $where = ['group' =>  'GROUP', 'status' => 1];
            $config = Db::table('a_fresh_config')->where($where)->select();
            $config =  array_column($config, 'value', 'name');
            Cache::store('redis_pintuan')->hashSet('group_pin_tuan_config', $config);
            Cache::store('redis_pintuan')->handler()->expire('group_pin_tuan_config', 180);
            if($key) return $config[$key];
            return $config;
        }
    }

}


?>
